package cs2340.spring13.team3.wheresmystuff.model;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

import cs2340.spring13.team3.wheresmystuff.support.JSONMessage;

/**
 * @author Tim Swihart <email obscured since this file is public>
 * @version 1.0.1
 * @since 2013-04-04
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public abstract class ServerObject implements Serializable {

	/**
	 * Eclipse generated serial version uid
	 */
	private static final long serialVersionUID = 873602981827944491L;
	protected static ObjectMapper om;
	protected static DefaultHttpClient http;
	protected static final String ROOT_URL = "https://immense-temple-7838.herokuapp.com";
	protected int id;

	public abstract JsonNode toJson();

	@JsonIgnore
	public static void setObjectMapper(ObjectMapper omap) {
		om = omap;
	}

	@JsonIgnore
	public static ObjectMapper getObjectMapper() {
		if (om == null) {
			om = new ObjectMapper();
		}
		return om;
	}

	@JsonIgnore
	public static void setHttpClient(DefaultHttpClient hc) {
		http = hc;
	}

	/**
	 * Synchronizes this object with the server, saving any
	 * changes/creating/deleting based on the passed in request
	 * 
	 * @throws SyncException
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	private void sync(HttpRequestBase req) throws SyncException,
			ClientProtocolException, IOException {
		HttpResponse response = http.execute(req);

		InputStream is = response.getEntity().getContent();
		if (is != null) {
			JSONMessage msg = new JSONMessage(getObjectMapper().readValue(is,
					JsonNode.class));

			if (msg.getErr().length() > 0) {
				throw new SyncException(msg.getErr());
			}
		}

	}

	/**
	 * Creates a destroy request and sends it to the server
	 * 
	 * @throws ClientProtocolException
	 * @throws SyncException
	 * @throws IOException
	 */
	protected void destroy() throws ClientProtocolException, SyncException,
			IOException {
		HttpDelete destroyReq = new HttpDelete(ROOT_URL + getBaseURL() + this.id);

		this.sync(destroyReq);
	}

	/**
	 * Creates and sends an update request to the server
	 * 
	 * @throws SyncException
	 * @throws JsonGenerationException
	 * @throws JsonMappingException
	 * @throws IOException
	 */
	public void update(String json) throws SyncException,
			JsonGenerationException, JsonMappingException, IOException {
		HttpPut updateReq = new HttpPut(ROOT_URL + getBaseURL() + this.getId());
		updateReq.setEntity(new StringEntity(json));

		updateReq.setHeader(HTTP.CONTENT_TYPE, "application/json");
		updateReq.setEntity(new ByteArrayEntity(json.getBytes("UTF-8")));

		updateReq.setHeader("Accept", "application/json");

		this.sync(updateReq);
	}

	/**
	 * Formats the Http request to create the specified ServerObject subclass
	 * POSTs to the proper url (BASEURL/CLASS/new)
	 * 
	 * @throws SyncException
	 *             with the error message from the server
	 * @throws JsonMappingException
	 *             if jackson encounters error deserializing into a JSONMessage
	 * @throws IOException
	 *             if the network has issues
	 */
	public void create(String json) throws SyncException, JsonMappingException,
			IOException {
		HttpPost createReq = new HttpPost(ROOT_URL + getBaseURL() + "new");
		createReq.setHeader(HTTP.CONTENT_TYPE, "application/json");
		createReq.setEntity(new ByteArrayEntity(json.getBytes("UTF-8")));

		createReq.setHeader("Accept", "application/json");

		this.sync(createReq);
	}

	/**
	 * Basic Exception subclass for errors concerning object syncing
	 * 
	 * @author Tim Swihart
	 * 
	 */
	public class SyncException extends Exception {

		/**
		 * auto-generated to make Eclipse happy
		 */
		private static final long serialVersionUID = -4794723417504642131L;

		public SyncException(String string) {
			super(string);
		}

	}

	public String getBaseURL() {
		return "";
	}

	@JsonProperty("id")
	public void setId(int id) {
		this.id = id;
	}

	@JsonProperty("id")
	public int getId() {
		return this.id;
	}

	public static DefaultHttpClient getHttp() {
		return ServerObject.http;
	}
}
